package cn.ibizlab.core.authentication.service.impl;

import cn.ibizlab.core.authentication.domain.AuthProvider;
import cn.ibizlab.core.authentication.service.AuthProviderService;
import cn.ibizlab.core.open.justauth.AuthCustomRequest;
import cn.ibizlab.core.open.justauth.AuthCustomSource;
import cn.ibizlab.core.open.justauth.JustAuthSource;
import cn.ibizlab.core.authentication.service.SocialService;
import cn.ibizlab.core.open.domain.OpenAccess;
import cn.ibizlab.core.open.domain.OpenAccessAuth;
import cn.ibizlab.util.common.ConfigParser;
import cn.ibizlab.util.common.JsonFieldExtractor;
import cn.ibizlab.util.enums.Entities;
import cn.ibizlab.util.errors.BadRequestAlertException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import me.zhyd.oauth.AuthRequestBuilder;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.util.Map;

import static cn.ibizlab.core.authentication.constants.AuthenticationConstant.*;

@Service
@ConditionalOnExpression("'${ibiz.auth.social.service:justAuth}'.equals('justAuth')")
public class JustAuthSocialService implements SocialService {

    @Autowired
    private AuthProviderService providerService;

    @Override
    public String authorize(OpenAccess socialConfig) {
        return getSocialAuthRequest(socialConfig).authorize(AuthStateUtils.createState());
    }

    @Override
    public OpenAccessAuth callback(OpenAccess socialConfig, String code, String state) {

        //构造社交平台认证请求
        AuthRequest authRequest = getSocialAuthRequest(socialConfig);
        AuthCallback callback = new AuthCallback();
        callback.setCode(code);
        callback.setState(state);

        //获取社交平台用户信息
        AuthResponse<AuthUser> response = authRequest.login(callback);
        // 判断授权响应是否成功
        if (!response.ok() || response.getData() == null) {
            throw new BadRequestAlertException(String.format("获取社交平台用户信息失败,原因:%1$s", response.getMsg()), Entities.AUTH_LOGIN.toString(), "Failed to obtain social platform user information");
        }
        String socialUserId;
        AuthUser authUser = response.getData();
        JSONObject userInfo = authUser.getRawUserInfo() != null? authUser.getRawUserInfo(): new JSONObject();


        //微信登录使用unionId作为userId
        if(PROVIDER_SOCIAL_WECHAT.equals(authUser.getSource()) && authUser.getToken()!=null && !ObjectUtils.isEmpty(authUser.getToken().getUnionId()))
            socialUserId = authUser.getToken().getUnionId();
        else
            socialUserId = authUser.getUuid();

        if(authUser.getToken()!=null) {
            userInfo.put("open_id",authUser.getToken().getOpenId());
            userInfo.put("union_id",authUser.getToken().getUnionId());
        }

        //构造认证用户
        OpenAccessAuth socialUser = new OpenAccessAuth()
                .setCredential(socialUserId)
                .setName(authUser.getUsername())
                .setMobile(userInfo.getString("mobile"))
                .setUid(authUser.getUuid())
                .setAvatar(authUser.getAvatar())
                .setMail(authUser.getEmail())
                .setAccessId(socialConfig.getId())
                .setParams(JSON.toJSONString(userInfo))
                .setToken(authUser.getToken().getAccessToken())
                .setRefreshToken(authUser.getToken().getRefreshToken());

        socialUser.set(PROVIDER_SOCIAL_TYPE,socialConfig.getOpenType());
        return socialUser;
    }

    private AuthRequestBuilder builder = AuthRequestBuilder.builder().extendSource(JustAuthSource.values());

    /**
     * 构造社交平台认证请求
     *
     * @param socialConfig
     * @return
     */
    @SneakyThrows
    protected synchronized AuthRequest getSocialAuthRequest(OpenAccess socialConfig) {

        if(socialConfig == null)
            throw new BadRequestAlertException("未传入社交平台认证配置",Entities.AUTH_LOGIN.toString(), "Social configuration not passed in");

        if(ObjectUtils.isEmpty(socialConfig.getOpenType()))
            throw new BadRequestAlertException("未传入社交平台类型",Entities.AUTH_LOGIN.toString(), "Social configuration type not passed in");

        AuthConfig authConfig = AuthConfig.builder()
                .clientId(socialConfig.getAccessKey())
                .clientSecret(socialConfig.getSecretKey())
                .authServerId(socialConfig.getRegionId())
                .ignoreCheckState(true)
                .build();

        String redirectUri = String.format("%1$s&id=%2$s", socialConfig.getRedirectUri(), socialConfig.getId());
        String appRedirectUri = getAppRedirectUri();
        //业务应用登录，拼接应用地址，社交认证成功后，uaa重定向至应用首页
        if (!ObjectUtils.isEmpty(appRedirectUri)){
            //redirectUri = String.format("%1$s&id=%2$s&redirect_uri=%3$s", socialConfig.getRedirectUri(), socialConfig.getId(), appRedirectUri);
            redirectUri = appRedirectUri;
        }
        else
            redirectUri = String.format("%1$s&id=%2$s", socialConfig.getRedirectUri(), socialConfig.getId());


        if(redirectUri!=null && (redirectUri.startsWith("http://") || redirectUri.startsWith("https://")))
            redirectUri = URLEncoder.encode(redirectUri, "UTF-8");
        //对重定向地址进行编码
        authConfig.setRedirectUri(redirectUri);

        //构造社交认证请求
        AuthRequest authRequest = null;
        try{
            authRequest = builder.source(socialConfig.getOpenType().toLowerCase()).authConfig(authConfig).build();
        }catch (Exception ex){}

        if(authRequest == null) {
            AuthProvider provider = providerService.get(socialConfig.getOpenType());
            Map baseConfig = ConfigParser.parseConfig(provider.getBaseConfig());
            AuthCustomSource source = new AuthCustomSource().setName(socialConfig.getOpenType().toLowerCase())
                    .setAccessTokenMethod((String)baseConfig.get("access_token_method")).setUserInfoMethod((String)baseConfig.get("user_info_method"))
                    .setAuthorizeUrl((String)baseConfig.get("authorize")).setRefreshUrl((String)baseConfig.get("refresh")).setAccessTokenUrl((String)baseConfig.get("access_token"))
                    .setRevokeUrl((String)baseConfig.get("revoke")).setUserInfoUrl((String)baseConfig.get("user_info")).setTargetClass(AuthCustomRequest.class)
                    .setMappingConfig(provider.getResponseConfig()).setSuccessFlag(provider.getResponseSuccessFlag());
            authRequest = new AuthCustomRequest(authConfig, source);
        }
        return authRequest;

    }

    /**
     * 应用重定向地址
     * @return
     */
    protected String getAppRedirectUri() {
        String redirectUri = null;
        if(RequestContextHolder.getRequestAttributes() != null && RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes){
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            redirectUri = request.getParameter("redirect_uri");
        }
        return redirectUri;
    }

}
